#!/bin/sh

PATCH_NAME="Kaspersky for Mac 26.0 - Patch B"

KAV_DIR="/Library/Application Support/Kaspersky Lab/KAV"
KAV_BINARIES_DIR="$KAV_DIR/Binaries"
KAVD_BINARIES_TAR="$KAV_BINARIES_DIR/binaries.tar.gz"
KAV_DATA_DIR="$KAV_DIR/Data"
KAV_APP_PKG="$KAV_DATA_DIR/kav_app.tar.gz"
KAV_AGENT_TAR="$KAV_DATA_DIR/kav_agent.tar.gz"
KAV_UPDATE_TAR="$KAV_DATA_DIR/kav_update.tar.gz"
KAV_CONFIGPDK_TAR="$KAV_DATA_DIR/configpdk.tar.gz"
KAV_UNINSTALLER_TAR="$KAV_DATA_DIR/kav_uninstaller.tar.gz"
KAV_AGENT_APP_PATH="$KAV_DIR/Applications/Kaspersky Anti-Virus Agent.app"
KAV_UPDATE_APP_PATH="$KAV_DIR/Applications/Kaspersky Anti-Virus Update.app"
KAV_UNINSTALLER_APP_PATH="$KAV_DIR/Applications/Kaspersky Uninstaller.app"
KAV_AGENT_WATCHPATH="$KAV_DIR/kickstart_agent"
LAUNCHAGENT_DIR="/Library/LaunchAgents"
LAUNCHAGENT_KAV_ID="com.kaspersky.kav.agent"
KAV_AGENT_PLIST="$LAUNCHAGENT_KAV_ID.plist"
KAV_AGENT_PLIST_BACKUP_DIR="$KAV_DIR"
KAVAPP_PATH="/Applications/Kaspersky Anti-Virus For Mac.app"
LOC_TGZ="$KAV_DATA_DIR/loc.tar.gz"
LOC_DIR="$KAV_DIR/Loc"
DOC_TGZ="$KAV_DATA_DIR/doc.tar.gz"
DOC_DIR="$KAV_DIR/Doc"
CONFIG_XML_CHANGES_TMP="$KAV_BINARIES_DIR/kav_config_xml_changes.v2"
CONFIG_XML="$KAV_BINARIES_DIR/config.xml"


install_log()
{
    logger -p 'install.warning' "Kaspersky.patch[$$] $@"
}

log()
{
    echo "`date '+%Y-%m-%d %H:%M:%S'` $@"
    install_log "$@"
}

log "${PATCH_NAME}"
log "${PATCH_VER}"

decrypt_file()
{
    local src="$1"
    local dst="$2"

    log "Decrypting '$src' '$dst'"

    openssl enc -d -aes-256-cbc -in "${src}" -out "${dst}" -md sha256 -k pass
}

get_decrypted_fname()
{
    basename "${1}"
}

get_value_by_xpath()
{
    # returns value as a string or "" in case of error or empty value
    /usr/bin/xmllint --xpath "$1" "$CONFIG_XML" 2>/dev/null
}

does_xpath_exist()
{
    # returns 1 when xpath exists and 0 when doesn't

    /usr/bin/xmllint --xpath "$1" "$CONFIG_XML" 1>/dev/null 2>/dev/null
    [ $? -eq 0 ] && echo 1 || echo 0
}

get_environment_key()
{
    get_value_by_xpath "//propertiesmap/key[@name=\"environment\"]/tSTRING[@name=\"$1\"]/text()"
}

get_last_patch_deployment()
{
    get_value_by_xpath "//propertiesmap/key[@name=\"Timestamps\"]/tDWORD[@name=\"LastPatchDeployment\"]/text()"
}

stop_kav_app()
{
    local pids=$(/bin/ps -axwo pid,command | /usr/bin/grep -v grep | /usr/bin/grep "Contents/MacOS/kav_app" | /usr/bin/awk '{print $1}')
    if [ "$pids" != "" ] ; then
        local oldIFS="$IFS"
        IFS=$'\n'
        for pid in `echo "$pids"`
        do
            log "Stopping kav_app with pid=$pid"
            /bin/kill -s KILL $pid
        done
        IFS="$oldIFS"
    fi
}

unload_agents()
{
    log "Disable and stop agents in launchctl ..."
    local records=$(/bin/ps -axwo uid,command | /usr/bin/grep -v grep | /usr/bin/grep loginwindow)
    if [ "$records" != "" ] ; then
        local oldIFS="$IFS"
        IFS=$'\n'
        for rec in `echo "$records"`
        do
            local uid=$(/bin/echo -n "$rec" | /usr/bin/awk '{print $1}')
            local target="gui/$uid/$LAUNCHAGENT_KAV_ID"

            # skip for initial 'loginwindow' dialog
            if [ "$uid" == "0" ] ; then
                log "Skip disabling agent when no user is logged on"
                continue
            fi

            /bin/launchctl disable "$target"
            log "Disabling agent for uid $uid finished with rc=$?"

            /bin/launchctl bootout "$target"
            log "bootout agent for uid $uid finished with rc=$?"
        done
        IFS="$oldIFS"
    fi

    # wait for stopping

    # 5 secs
    local counter=20
    while [ $counter -gt 0 ] ; do
        local uids=$(/bin/ps -axwo uid,command | /usr/bin/grep -v grep | /usr/bin/grep "Contents/MacOS/kav_agent" | /usr/bin/awk '{print $1}')
        if [ -z "$uids" ] ; then
            log "No agents to stop"
            break
        fi

        log "Wait for stopping agent for uids = $uids ... "
        sleep 0.25
        ((counter-=1))
    done

    [ $counter -gt 0 ] && return 0

    log "Unable to stop all agents."
    return 1
}

do_killall()
{
    local app_name="$1"

    local pids=$(/bin/ps -axwo pid,command | /usr/bin/grep -v grep | /usr/bin/grep "Contents/MacOS/$app_name" | /usr/bin/awk '{print $1}')
    if [ "$pids" != "" ] ; then
        local oldIFS="$IFS"
        IFS=$'\n'
        for pid in `echo "$pids"`
        do
            log "Stopping '$app_name' with pid=$pid"
            /bin/kill -s KILL $pid
        done
        IFS="$oldIFS"
    fi
}

kill_kav_apps()
{
    do_killall "kav_app"
}

kill_agents()
{
    do_killall "kav_agent"
}

disable_kav_agents()
{
    unload_agents && return 0

    # try to stop hung agents through killing
    log "Kill agents and try to unload again"
    kill_agents

    unload_agents
}

enable_kav_agents()
{
    local plist_path="$LAUNCHAGENT_DIR/$KAV_AGENT_PLIST"

    log "Restarting agents..."
    local records=$(/bin/ps -axwo uid,command | /usr/bin/grep -v grep | /usr/bin/grep loginwindow)
    if [ "$records" != "" ] ; then
        local oldIFS="$IFS"
        IFS=$'\n'
        for rec in `echo "$records"`
        do
            local uid=$(/bin/echo -n "$rec" | /usr/bin/awk '{print $1}')

            # skip for initial 'loginwindow' dialog
            if [ "$uid" == "0" ] ; then
                log "Skip starting agent when no user is logged on"
                continue
            fi

            /bin/launchctl enable "gui/$uid/$LAUNCHAGENT_KAV_ID"
            log "Enabling agent for uid=$uid with rc=$?"

            /bin/launchctl asuser "$uid" /bin/launchctl list | /usr/bin/grep -q "$LAUNCHAGENT_KAV_ID"
            if [ $? -eq 0 ] ; then
                log "Starting already loaded agent for uid $uid ..."
                /bin/launchctl asuser "$uid" /bin/launchctl start "$LAUNCHAGENT_KAV_ID" \
                    || log "Unable to start agent $LAUNCHAGENT_KAV_ID"
            else
                log "Loading agent for uid $uid ..."
                /bin/launchctl bootstrap "gui/$uid" "$plist_path" \
                    || log "Unable to load agent $plist_path"
            fi
        done
        IFS="$oldIFS"
    fi
    log "Kickstarting agent..."
    /usr/bin/touch "$KAV_AGENT_WATCHPATH"
}

extract_bundle_tar()
{
    local TAR_PATH="$1"
    local BUNDLE_PATH="$2"
    local BUNDLE_NAME="$3"
    local BUNDLE_DESTINATION_PATH="$4"
    local MATCHED_ARCHIVE="$5"

    if [ ! -f "${TAR_PATH}" ]; then
        log "No ${TAR_PATH} tar, skip ${BUNDLE_NAME} extraction"
        return 0
    fi

    local TAR_TIMESTAMP=$(/usr/bin/stat -f "%m" "$TAR_PATH")
    local LAST_PATCH_DEPLOYMENT_TIMESTAMP=$(get_last_patch_deployment)
    log "${TAR_PATH} timestamp: $TAR_TIMESTAMP, last path deployment timestamp: $LAST_PATCH_DEPLOYMENT_TIMESTAMP"

    if [[ (-e "${TAR_PATH}") && ($TAR_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]];
    then
        local ENC_SRC_PATH="${TAR_PATH}"

        log "Decrypting $ENC_SRC_PATH ..."
        local DECRYPTED_TEMP_DIR=$(mktemp -d)
        local DECRYPTED_FNANE=$(get_decrypted_fname "${ENC_SRC_PATH}")
        local DECRYPTED_FULL_PATH="${DECRYPTED_TEMP_DIR}/${DECRYPTED_FNANE}"
        decrypt_file "${ENC_SRC_PATH}" "${DECRYPTED_FULL_PATH}" || return 1

        local EXTRACTION_TEMP_PATH=$(mktemp -d)

        log "Extracting ${BUNDLE_NAME} resources to $EXTRACTION_TEMP_PATH..."
        /usr/bin/tar -xzvf "${DECRYPTED_FULL_PATH}" -C "${EXTRACTION_TEMP_PATH}"
        log "Extracted: "
        /bin/ls -la "$EXTRACTION_TEMP_PATH"

        log "Deploying ${BUNDLE_NAME} resources..."

        if [ -f "$EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" ]; then
            local CURRENT_BUNDLE_EXTRACTION_TEMP_PATH=$(mktemp -d)
            log "Extracting application resources from $MATCHED_ARCHIVE to $CURRENT_BUNDLE_EXTRACTION_TEMP_PATH..."
            /usr/bin/tar -xzvf "$EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" -C "$CURRENT_BUNDLE_EXTRACTION_TEMP_PATH"

            local CURRENT_BUNDLE_RESOURSES_TEMP_PATH=$(mktemp -d)

            log "Saving current bundle resources to ${CURRENT_BUNDLE_RESOURSES_TEMP_PATH}"
            /bin/cp -rfv "${BUNDLE_PATH}/Contents/Resources" "${CURRENT_BUNDLE_RESOURSES_TEMP_PATH}"

            log "Deleting current bundle"
            /bin/rm -rfv "${BUNDLE_PATH}"

            log "Copying ${CURRENT_BUNDLE_EXTRACTION_TEMP_PATH}/${BUNDLE_NAME} to ${BUNDLE_DESTINATION_PATH}"
            /bin/cp -rvf "${CURRENT_BUNDLE_EXTRACTION_TEMP_PATH}/${BUNDLE_NAME}" "${BUNDLE_DESTINATION_PATH}"

            log "Deleting ${BUNDLE_PATH}/Contents/Resources"
            /bin/rm -rfv "${BUNDLE_PATH}/Contents/Resources"

            log "Moving ${CURRENT_BUNDLE_RESOURSES_TEMP_PATH}/Resources to ${BUNDLE_PATH}/Contents"
            /bin/mv "${CURRENT_BUNDLE_RESOURSES_TEMP_PATH}/Resources" "${BUNDLE_PATH}/Contents"

            log "Copying new resources to ${BUNDLE_PATH}/Contents/Resources"
            /bin/cp -Rfv "${CURRENT_BUNDLE_EXTRACTION_TEMP_PATH}/${BUNDLE_NAME}/Contents/Resources/"* "${BUNDLE_PATH}/Contents/Resources"

            /usr/bin/touch "$BUNDLE_PATH"
        else
            log "No matched archive in $EXTRACTION_TEMP_PATH, do not apply patch for $BUNDLE_PATH"
        fi
    fi

    return 0
}

add_value_to_config_changes_file()
{
    local KEY="$1"
    local VALUE="$2"

    # use '*' to update field which can not exist
    local PATTERN="$3"

    # e.g. "profiles.LocalizationManager.settings"
    local PATH_TO_NODE="$4"

    # Used on creation a new field. Supported only: tBOOL, tDWORD, tSTRING
    local TYPE="$5"

    if [ "$TYPE" != "tBOOL" -a "$TYPE" != "tDWORD" ];
    then
        TYPE="tSTRING"
    fi

    local SEARCH_RESULT=$(/usr/bin/grep "${PATTERN}" "$CONFIG_XML")
    if [ -n "$SEARCH_RESULT" ];
    then
        log "Skip updating ${KEY} value"
    else
        log "Updating in node '${PATH_TO_NODE}'  ${KEY} value to ${VALUE} with type ${TYPE}"
        /usr/bin/touch "$CONFIG_XML_CHANGES_TMP"
        /bin/echo "${PATH_TO_NODE} ${KEY} ${VALUE} ${TYPE}" >> "$CONFIG_XML_CHANGES_TMP"
    fi

    return 0
}

add_env_value_to_update()
{
    add_value_to_config_changes_file "$1" "$2" "$3" "environment"
}

patch_loc()
{
    LOC_TGZ_TIMESTAMP=$(/usr/bin/stat -f "%m" "$LOC_TGZ")
    log "loc timestamp: $LOC_TGZ_TIMESTAMP"
    if [[ -e "$LOC_TGZ" && ($LOC_TGZ_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]];
    then
        LOC_EXTRACTION_TEMP_PATH=$(mktemp -d)
        log "Extracting $LOC_TGZ into $LOC_EXTRACTION_TEMP_PATH"
        /usr/bin/tar -xzvf "$LOC_TGZ" -C "$LOC_EXTRACTION_TEMP_PATH"
        log "Extracted: "
        /bin/ls -la "$LOC_EXTRACTION_TEMP_PATH"

        log "Deploying localizations..."
        if [ -f "$LOC_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" ]; then
            CURRENT_LOC_EXTRACTION_TEMP_PATH=$(mktemp -d)
            log "Extracting application resources from $MATCHED_ARCHIVE to $CURRENT_LOC_EXTRACTION_TEMP_PATH..."
            /usr/bin/tar -xzvf "$LOC_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" -C "$CURRENT_LOC_EXTRACTION_TEMP_PATH"
            /bin/cp -Rfv "$CURRENT_LOC_EXTRACTION_TEMP_PATH"/ "$LOC_DIR"
        else
            log "No matched archive in $LOC_EXTRACTION_TEMP_PATH, do not apply patch for $LOC_DIR"
        fi
    else
        log "Skip extracting loc.tar.gz"
    fi
}

patch_doc()
{
    DOC_TGZ_TIMESTAMP=$(/usr/bin/stat -f "%m" "$DOC_TGZ")
    log "doc.tar.gz timestamp: $DOC_TGZ_TIMESTAMP"
    if [[ -e "$DOC_TGZ" && ($DOC_TGZ_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]];
    then
        DOC_EXTRACTION_TEMP_PATH=$(mktemp -d)

        log "Extracting $DOC_TGZ into $DOC_EXTRACTION_TEMP_PATH"
        /usr/bin/tar -xzvf "$DOC_TGZ" -C "$DOC_EXTRACTION_TEMP_PATH"
        log "Extracted: "
        /bin/ls -la "$DOC_EXTRACTION_TEMP_PATH"

        log "Deploying docs..."
        if [ -f "$DOC_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" ]; then
            CURRENT_DOC_EXTRACTION_TEMP_PATH=$(mktemp -d)
            log "Extracting application resources from $MATCHED_ARCHIVE to $CURRENT_DOC_EXTRACTION_TEMP_PATH..."
            /usr/bin/tar -xzvf "$DOC_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" -C "$CURRENT_DOC_EXTRACTION_TEMP_PATH"
            /bin/cp -Rfv "$CURRENT_DOC_EXTRACTION_TEMP_PATH"/ "$DOC_DIR"
        else
            log "No matched archive in $DOC_EXTRACTION_TEMP_PATH, do not apply patch for $DOC_DIR"
        fi
    else
        log "Skip extracting doc.tar.gz"
    fi
}

patch_binaries()
{
    BINARIES_TGZ_TIMESTAMP=$(/usr/bin/stat -f "%m" "$KAVD_BINARIES_TAR")
    log "binaries.tar.gz timestamp: $BINARIES_TGZ_TIMESTAMP"
    
    if [[ -e "$KAVD_BINARIES_TAR" && ($BINARIES_TGZ_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]];
    then
        local EXTRACTION_TEMP_PATH=$(mktemp -d)

        log "Extracting $KAVD_BINARIES_TAR to $EXTRACTION_TEMP_PATH ..."
        /usr/bin/tar -xzvf "$KAVD_BINARIES_TAR" -C "$EXTRACTION_TEMP_PATH" --no-same-owner
        /bin/mv -fv "$EXTRACTION_TEMP_PATH"/* "$KAV_BINARIES_DIR"
        /bin/rm -rf "$EXTRACTION_TEMP_PATH"
    else
        log "Skip extracting binaries.tar.gz"
    fi
}

patch_kav_app()
{
    KAV_APP_PKG_TIMESTAMP=$(/usr/bin/stat -f "%m" "$KAV_APP_PKG")
    log "kav app pkg timestamp: $KAV_APP_PKG_TIMESTAMP, last path deployment timestamp: $LAST_PATCH_DEPLOYMENT_TIMESTAMP"

    if [[ (-e "$KAV_APP_PKG") && ($KAV_APP_PKG_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]]; then

        local ENC_SRC_PATH="${KAV_APP_PKG}"

        log "Decrypting $ENC_SRC_PATH ..."
        local DECRYPTED_TEMP_DIR=$(mktemp -d)
        local DECRYPTED_FNANE=$(get_decrypted_fname "${ENC_SRC_PATH}")
        local DECRYPTED_FULL_PATH="${DECRYPTED_TEMP_DIR}/${DECRYPTED_FNANE}"
        decrypt_file "${ENC_SRC_PATH}" "${DECRYPTED_FULL_PATH}" || return 1

        KAV_APP_EXTRACTION_TEMP_PATH=$(mktemp -d)

        log "Extracting $DECRYPTED_FULL_PATH into $KAV_APP_EXTRACTION_TEMP_PATH"
        /usr/bin/tar -xzvf "$DECRYPTED_FULL_PATH" -C "$KAV_APP_EXTRACTION_TEMP_PATH"
        log "Extracted: "
        /bin/ls -la "$KAV_APP_EXTRACTION_TEMP_PATH"

        log "Deploying application resources..."

        if [ -f "$KAV_APP_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" ]; then
            CURRENT_KAV_APP_EXTRACTION_TEMP_PATH=$(mktemp -d)
            log "Extracting application resources from $MATCHED_ARCHIVE to $CURRENT_KAV_APP_EXTRACTION_TEMP_PATH..."
            /usr/bin/tar -xzvf "$KAV_APP_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" -C "$CURRENT_KAV_APP_EXTRACTION_TEMP_PATH"

            KAV_APP_CUR_CONTENTS_TEMP_PATH=$(mktemp -d)
            log "Saving current app embedded.provisionprofile to ${KAV_APP_CUR_CONTENTS_TEMP_PATH}"
            /bin/cp -fv "${KAVAPP_PATH}/Contents/embedded.provisionprofile" "${KAV_APP_CUR_CONTENTS_TEMP_PATH}"

            KAV_APP_CUR_RES_TEMP_PATH=$(mktemp -d)

            log "Saving current app resources to ${KAV_APP_CUR_RES_TEMP_PATH}"
            /bin/cp -rfv "${KAVAPP_PATH}/Contents/Resources" "${KAV_APP_CUR_RES_TEMP_PATH}"

            KAV_APP_SAFARI_EXT_CUR_RES_TEMP_PATH=$(mktemp -d)

            log "Saving current safari extension resources to ${KAV_APP_SAFARI_EXT_CUR_RES_TEMP_PATH}"
            /bin/cp -rfv "${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents/Resources" "${KAV_APP_SAFARI_EXT_CUR_RES_TEMP_PATH}"

            # remove kav_app binary to prevent races on loading ( e.g. on autostarting )
            log "Deleting current app binary"
            /bin/rm -v "${KAVAPP_PATH}/Contents/MacOS/kav_app"

            log "Stopping application..."
            stop_kav_app

            log "Deleting current app bundle"
            /bin/rm -rfv "${KAVAPP_PATH}"

            KAV_APP_PARENT_DIR="${KAVAPP_PATH}"
            log "Moving ${CURRENT_KAV_APP_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus For Mac.app to ${KAV_APP_PARENT_DIR}"
            /bin/cp -rvf "${CURRENT_KAV_APP_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus For Mac.app" "${KAV_APP_PARENT_DIR}"

            log "Updating app resources"

            log "Deleting ${KAVAPP_PATH}/Contents/Resources"
            /bin/rm -rfv "${KAVAPP_PATH}/Contents/Resources"
            
            log "Moving ${KAV_APP_CUR_CONTENTS_TEMP_PATH}/embedded.provisionprofile to ${KAVAPP_PATH}/Contents"
            mv "${KAV_APP_CUR_CONTENTS_TEMP_PATH}/embedded.provisionprofile" "${KAVAPP_PATH}/Contents"

            log "Moving ${KAV_APP_CUR_RES_TEMP_PATH}/Resources to ${KAVAPP_PATH}/Contents"
            mv "${KAV_APP_CUR_RES_TEMP_PATH}/Resources" "${KAVAPP_PATH}/Contents"

            log "Copying new resources to app"
            /bin/cp -Rfv "${CURRENT_KAV_APP_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus For Mac.app/Contents/Resources/"* "${KAVAPP_PATH}/Contents/Resources"

            log "Updating safari extension resources"

            log "Deleting ${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents/Resources"
            /bin/rm -rfv "${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents/Resources"

            log "Moving ${KAV_APP_SAFARI_EXT_CUR_RES_TEMP_PATH}/Resources to ${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents"
            mv "${KAV_APP_SAFARI_EXT_CUR_RES_TEMP_PATH}/Resources" "${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents"

            log "Copying new resources to app"
            /bin/cp -Rfv "${CURRENT_KAV_APP_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus For Mac.app/Contents/PlugIns/KasperskySecurity.appex/Contents/Resources/"* "${KAVAPP_PATH}/Contents/PlugIns/KasperskySecurity.appex/Contents/Resources"

            /usr/bin/touch "$KAVAPP_PATH"

            #lock KAV application bundle so it can not be moved or deleted (copying is still available tho)
            log "Lock \"$KAVAPP_PATH\"..."
            chflags -v uchg "$KAVAPP_PATH"
            if [ $? != 0 ]
            then
                log "Failed to change flags of \"$KAVAPP_PATH\""
                return 1
            else
                log "Flags of \"$KAVAPP_PATH\" changed successfully"
            fi
        else
            log "No matched archive in $KAV_APP_EXTRACTION_TEMP_PATH, do not apply patch for $KAVAPP_PATH"
        fi
    fi
}

patch_kav_agent()
{
    KAV_AGENT_PKG_TIMESTAMP=$(/usr/bin/stat -f "%m" "$KAV_AGENT_TAR")
    log "kav app pkg timestamp: $KAV_AGENT_PKG_TIMESTAMP, last path deployment timestamp: $LAST_PATCH_DEPLOYMENT_TIMESTAMP"

    if [[ (-e "$KAV_AGENT_TAR") && ($KAV_AGENT_PKG_TIMESTAMP > $LAST_PATCH_DEPLOYMENT_TIMESTAMP) ]];
    then

        local ENC_SRC_PATH="${KAV_AGENT_TAR}"

        log "Decrypting $ENC_SRC_PATH ..."
        local DECRYPTED_TEMP_DIR=$(mktemp -d)
        local DECRYPTED_FNANE=$(get_decrypted_fname "${ENC_SRC_PATH}")
        local DECRYPTED_FULL_PATH="${DECRYPTED_TEMP_DIR}/${DECRYPTED_FNANE}"
        decrypt_file "${ENC_SRC_PATH}" "${DECRYPTED_FULL_PATH}" || return 1


        KAV_AGENT_EXTRACTION_TEMP_PATH=$(mktemp -d)

        log "Extracting agent resources to $KAV_AGENT_EXTRACTION_TEMP_PATH..."
        /usr/bin/tar -xzvf "$DECRYPTED_FULL_PATH" -C "$KAV_AGENT_EXTRACTION_TEMP_PATH"
        log "Extracted: "
        /bin/ls -la "$KAV_AGENT_EXTRACTION_TEMP_PATH"

        log "Deploying agent resources..."

        if [ -f "$KAV_AGENT_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" ]; then
            CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH=$(mktemp -d)
            log "Extracting application resources from $MATCHED_ARCHIVE to $CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH..."
            /usr/bin/tar -xzvf "$KAV_AGENT_EXTRACTION_TEMP_PATH/$MATCHED_ARCHIVE" -C "$CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH"

            KAV_AGENT_CUR_RES_TEMP_PATH=$(mktemp -d)

            log "Saving current agent resources to ${KAV_AGENT_CUR_RES_TEMP_PATH}"
            /bin/cp -rfv "${KAV_AGENT_APP_PATH}/Contents/Resources" "${KAV_AGENT_CUR_RES_TEMP_PATH}"

            log "Deleting current agent"
            /bin/rm -rfv "${KAV_AGENT_APP_PATH}"

            log "Moving ${CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus Agent.app to ${KAV_DIR}/Applications"
            /bin/cp -rvf "${CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus Agent.app" "${KAV_DIR}/Applications"

            log "Deleting ${KAV_AGENT_APP_PATH}/Contents/Resources"
            /bin/rm -rfv "${KAV_AGENT_APP_PATH}/Contents/Resources"

            log "Moving ${KAV_AGENT_CUR_RES_TEMP_PATH}/Resources to ${KAV_AGENT_APP_PATH}/Contents"
            mv "${KAV_AGENT_CUR_RES_TEMP_PATH}/Resources" "${KAV_AGENT_APP_PATH}/Contents"

            log "Copying new resources to agent"
            /bin/cp -Rfv "${CURRENT_KAV_AGENT_EXTRACTION_TEMP_PATH}/Kaspersky Anti-Virus Agent.app/Contents/Resources/"* "${KAV_AGENT_APP_PATH}/Contents/Resources"

            /usr/bin/touch "${KAV_AGENT_APP_PATH}"
        else
            log "No matched archive in $KAV_AGENT_EXTRACTION_TEMP_PATH, do not apply patch for $KAV_AGENT_APP_PATH"
        fi
    fi
}

patch_updater()
{
    extract_bundle_tar "${KAV_UPDATE_TAR}" "${KAV_UPDATE_APP_PATH}" "Kaspersky Anti-Virus Update.app" "${KAV_DIR}/Applications" "${MATCHED_ARCHIVE}"
        [ $? != 0 ] && log "warning: failed to extract ${KAV_UPDATE_TAR}"
}

patch_uninstaller()
{
    extract_bundle_tar "${KAV_UNINSTALLER_TAR}" "${KAV_UNINSTALLER_APP_PATH}" "Kaspersky Uninstaller.app" "${KAV_DIR}/Applications" "${MATCHED_ARCHIVE}"
        [ $? != 0 ] && log "warning: failed to extract ${KAV_UNINSTALLER_TAR}"
}

update_FollowSymlinks_in_Scan_Startup()
{
    # FIXME: rework to remove copy-pasted code

    # ###################################
    # fix fields in default settings
    local max_num=9999
    for n in $(seq 0 $max_num)
    do
        local fn=$(printf "%04d" "$n")

        local xpath="//propertiesmap/key[@name=\"profiles\"]/key[@name=\"Scan_Startup\"]/key[@name=\"settings\"]/key[@name=\"def\"]/key[@name=\"ScanObjects\"]/key[@name=\"$fn\"]/tBOOL[@name=\"FollowSymlinks\"]/text()"

        local exist=$(does_xpath_exist "$xpath")
        [ $exist != 1 ] && break

        # check if a field is already set
        local field_key="FollowSymlinks"
        local dot_path="profiles.Scan_Startup.settings.def.ScanObjects.$fn"

        local val=$(get_value_by_xpath "$xpath")
        if [ "$val" == "1" ]; then
            log "Skip updating '$field_key' value in '$dot_path'"
            continue
        fi

        add_value_to_config_changes_file "$field_key" "1" "*" "$dot_path" "tBOOL"
    done

    # ###################################
    # fix fields in custom settings
    local max_num=9999
    for n in $(seq 0 $max_num)
    do
        local fn=$(printf "%04d" "$n")

        local xpath="//propertiesmap/key[@name=\"profiles\"]/key[@name=\"Scan_Startup\"]/key[@name=\"settings\"]/key[@name=\"ScanObjects\"]/key[@name=\"$fn\"]/tBOOL[@name=\"FollowSymlinks\"]/text()"

        local exist=$(does_xpath_exist "$xpath")
        [ $exist != 1 ] && break

        # check if a field is already set
        local field_key="FollowSymlinks"
        local dot_path="profiles.Scan_Startup.settings.ScanObjects.$fn"

        local val=$(get_value_by_xpath "$xpath")
        if [ "$val" == "1" ]; then
            log "Skip updating '$field_key' value in '$dot_path'"
            continue
        fi

        add_value_to_config_changes_file "$field_key" "1" "*" "$dot_path" "tBOOL"
    done

}

update_config_xml()
{
    log "Updating config.xml"

    add_env_value_to_update "UcpUisUrl" "https://uis.kaspersky.com/" "UcpUisUrl.*uis.kaspersky.com"
    add_env_value_to_update "UcpUisUrlNoCA" "https://web.uis.kaspersky.com/" "UcpUisUrlNoCA.*web.uis.kaspersky.com"
    add_env_value_to_update "UcpUisRealm" "https://ucp.kaspersky-labs.com/" "UcpUisRealm.*ucp.kaspersky-labs.com"
    add_env_value_to_update "UcpSupportedAuthFactors" "Phone,OtpGenerator" "UcpSupportedAuthFactors"
    add_env_value_to_update "PasswordRecoveryLink" "https://www.kaspersky-help.com/?hl=[localization]&link=ucppassword&syst=[systemVersion]&pid=[tierType]-mac&version=[ApplicationVersion]" "PasswordRecoveryLink.*\[tierType\]-mac"
    add_env_value_to_update "PrivacyStatementLink" "https://www.kaspersky-help.com/?hl=[localization]&link=ucpconditions&syst=[systemVersion]&pid=[tierType]-mac&version=[ApplicationVersion]" "PrivacyStatementLink.*\[tierType\]-mac"
    add_env_value_to_update "KSCPersonalTryLink" "https://redirect.kaspersky.com/sales?type=trykscpersonal&act-pid=[tierType]-mac&act-pv=[act-pv]&act-local=[remappedLocalization]&region=[systemRegion]&customization=[RebrandingCode]&act-serial=[act-serial]&lic-id=[act-licenseid]&act-liccount=[act-liccount]&act-lic-type=[act-lic-type]&act-lic-lifespan=[act-lic-lifespan]&act-lic-daystillexpiration=[act-lic-daystillexpiration]&act-item=[ActionUiControlName]&rpe=1" "KSCPersonalTryLink.*\[tierType\]-mac"
    add_env_value_to_update "KSCPersonalBuyLink" "https://redirect.kaspersky.com/sales?type=buykscpersonal&act-pid=[tierType]-mac&act-pv=[act-pv]&act-local=[remappedLocalization]&region=[systemRegion]&customization=[RebrandingCode]&act-serial=[act-serial]&lic-id=[act-licenseid]&act-liccount=[act-liccount]&act-lic-type=[act-lic-type]&act-lic-lifespan=[act-lic-lifespan]&act-lic-daystillexpiration=[act-lic-daystillexpiration]&act-item=[ActionUiControlName]&rpe=1" "KSCPersonalBuyLink.*\[tierType\]-mac"
    add_env_value_to_update "APMobileLink" "https://click.kaspersky.com/?hl=[Localization]&customization=[RebrandingCode]&link=apmobile&syst=[OsVersion]&pid=[tierType]-mac&version=[ApplicationVersion]&hotfix=[GlobalProductHotfix]" "APMobileLink.*\[tierType\]-mac"
    add_env_value_to_update "ForumLink" "https://www.kaspersky-help.com/?hl=[act-local]&link=community&syst=[systemVersion]&pid=[tierType]-mac&version=[productVersion]&hotfix=[productHotfix]" "ForumLink.*\[tierType\]-mac"
    add_env_value_to_update "SupportLink" "https://www.kaspersky-help.com/?hl=[act-local]&link=kb&syst=[systemVersion]&pid=[tierType]-mac&version=[productVersion]&hotfix=[productHotfix]" "SupportLink.*\[tierType\]-mac"
    add_env_value_to_update "VirusLink" "https://click.kaspersky.com/?hl=[Localization]&customization=[RebrandingCode]&link=securelist&syst=[OsVersion]&pid=[tierType]-mac&version=[ApplicationVersion]&hotfix=[GlobalProductHotfix]&vn=[VirusName]" "VirusLink.*\[tierType\]-mac"
    add_env_value_to_update "CommonRedirectorParams" "act-pid=[tierType]-mac&act-pv=[applicationVersion]&hl=[localization]&act-liccount=[act-liccount]&act-lic-type=[licenseType]&act-lic-lifespan=[licenseTerm]&act-lic-daystillexpiration=[licenseDaysToExpiration]&act-lic-dayssinceinstall=[licenseDaysSinceActivation]&act-lic-dse=[licenseDaysAfterExpiration]&act-lic-status=[LicenseStatusId]" "CommonRedirectorParams.*\[tierType\]-mac"
    add_env_value_to_update "HardDriveSMARTInfoLink" "https://click.kaspersky.com/?hl=[localization]&customization=[RebrandingCode]&link=hard_drive_smart_info&syst=[systemVersion]&pid=[tierType]-mac&version=[applicationVersion]&hotfix=[productHotfix]" "HardDriveSMARTInfoLink.*\[tierType\]-mac"
    add_env_value_to_update "HardDriveDiskBad" "https://click.kaspersky.com/?hl=[localization]&customization=[RebrandingCode]&link=hard_drive_disk_bad&syst=[systemVersion]&pid=[tierType]-mac&version=[applicationVersion]&hotfix=[productHotfix]" "HardDriveDiskBad.*\[tierType\]-mac"
    add_env_value_to_update "HomeNetworkUnknownDeviceLink" "https://click.kaspersky.com/?hl=[localization]&customization=[RebrandingCode]&link=home_device_protection&syst=[systemVersion]&pid=[tierType]-mac&version=[applicationVersion]&hotfix=[productHotfix]" "HomeNetworkUnknownDeviceLink.*\[tierType\]-mac"
    add_env_value_to_update "OnlineHelpLink" "https://click.kaspersky.com/?hl=[remappedLocalization]&customization=[RebrandingCode]&link=online_help&ext_reason=[InvalidReason]&syst=[OsVersion]&pid=[tierType]-mac&version=[ApplicationVersion]&hotfix=[GlobalProductHotfix]&helpid=[helpId]" "OnlineHelpLink.*\[tierType\]-mac"
    add_env_value_to_update "ComponentsNotRunningLink" "https://click.kaspersky.com/?hl=[Localization]&customization=[RebrandingCode]&link=componentsnotrunning&ext_reason=[InvalidReason]&syst=[OsVersion]&pid=[tierType]-mac&version=[ApplicationVersion]&hotfix=[GlobalProductHotfix]" "ComponentsNotRunningLink.*\[tierType\]-mac"
    add_env_value_to_update "ActivationErrorFAQLink" "https://click.kaspersky.com/?hl=[Localization]&customization=[RebrandingCode]&link=activationerror&ext_reason=[InvalidReason]&syst=[OsVersion]&pid=[tierType]-mac&version=[ApplicationVersion]&hotfix=[GlobalProductHotfix]" "ActivationErrorFAQLink.*\[tierType\]-mac"

    ####### update non environment fields ######
    # patch existed fields
    # ###################################

    update_FollowSymlinks_in_Scan_Startup

    # ###################################
    # add non-existed fields
    # ###################################
    add_value_to_config_changes_file "ScanArchived" "1" "*" "profiles.Scan_Startup.settings" "tBOOL"
    add_value_to_config_changes_file "AskPassword" "0" "*" "profiles.Scan_Startup.settings" "tBOOL"

    # for 26.0-TR only:
    local raw_region=$(get_environment_key "ProductRegionRaw")
    if [ -z "$raw_region" ]
    then
        add_value_to_config_changes_file "ProductRegionRaw" "$PRODUCT_REGION_RAW" "*" "environment" "tSTRING"
    fi

    # trace prepared change list
    if [ -f "${CONFIG_XML_CHANGES_TMP}" ];
    then
        log "Printing $CONFIG_XML_CHANGES_TMP"
        /bin/cat "$CONFIG_XML_CHANGES_TMP"
    else
        log "No ${CONFIG_XML_CHANGES_TMP} file"
    fi
}

# seems it is obsolete since 23+
stop_sysextctrld()
{
    SYSCTRLD_PID=$(/bin/ps -axwo pid,command | /usr/bin/grep -v grep | /usr/bin/grep "${KAVAPP_PATH}/Contents/MacOS/sysextctrld.app/Contents/MacOS/sysextctrld" | /usr/bin/awk '{print $1}')
    if [ "${SYSCTRLD_PID}" != "" ]
    then
        log "System extension controller pid: ${SYSCTRLD_PID}"
        /bin/kill -s TERM "${SYSCTRLD_PID}"
    else
        log "warning: Process sysextctrld not found"
    fi
}

patch_configpdk()
{
    if [ ! -e "${KAV_CONFIGPDK_TAR}" ] ; then
        log "No configpdk. skipping."
        return 0
    fi

    log "Extracting configpdk to $KAV_BINARIES_DIR..."
    /usr/bin/tar -xzvf "$KAV_CONFIGPDK_TAR" -C "$KAV_BINARIES_DIR"

    log "Changing esm owner..."
    /usr/sbin/chown root:admin "$KAV_BINARIES_DIR"/configpdk_x86_64.esm
    /usr/sbin/chown root:admin "$KAV_BINARIES_DIR"/configpdk_arm64.esm
}

delete_old_stuff()
{
    log "Deleting personal certificates"
    /bin/rm -rfv /Library/Caches/com.kaspersky.kav/PersonalCertificates/*

    local RMVERBARG="-v"
    local CRASH_FILES_DIR="Library/Logs/DiagnosticReports"
    USERS_DIR="/Users"

    log "Removing kav crash logs from \"/$CRASH_FILES_DIR\"..."
    rm $RMVERBARG -f "/$CRASH_FILES_DIR/"{Retired,}/kav*
    rm $RMVERBARG -f "/$CRASH_FILES_DIR/"{Retired,}/kavd*
    rm $RMVERBARG -f "/$CRASH_FILES_DIR/"{Retired,}/com.kaspersky.kav.sysext*
    for USERHOME in $(ls "$USERS_DIR")
    do
        log "Removing kav crash logs from \"$USERS_DIR/$USERHOME/$CRASH_FILES_DIR\"..."
        rm $RMVERBARG -f "$USERS_DIR/$USERHOME/$CRASH_FILES_DIR/"{Retired,}/kav_app*
        rm $RMVERBARG -f "$USERS_DIR/$USERHOME/$CRASH_FILES_DIR/"{Retired,}/kav_agent*
        rm $RMVERBARG -f "$USERS_DIR/$USERHOME/$CRASH_FILES_DIR/"{Retired,}/KasperskySecurity*
    done
}

get_product_region_raw()
{
    local region=""

    for tmp in $(seq 1 1)
    do
        # check since 26.0a
        region=$(get_environment_key "ProductRegionRaw")
        if [ -n  "$region" ] ; then
            break
        fi

        # check on 26.0 TR
        local md5_26_0tr_TW="34332726b4f9dabafa6899095ef76b66" 
        local md5_26_0tr_INT_SA="78f9371642cc4aca17a373cd9459b42f" 

        local agent_path="/Library/Application Support/Kaspersky Lab/KAV/Applications/Kaspersky Anti-Virus Agent.app/Contents/MacOS/kav_agent"
        local md5_current=$(md5 -q "$agent_path")

        if [ "$md5_current" = "$md5_26_0tr_TW" ] ; then
            region="TW"
            break 
        elif [ "$md5_current" = "$md5_26_0tr_INT_SA" ] ; then
            region="INT_SA"
            break 
        fi

        # default check
        region=$(get_environment_key "ProductRegion")
    done

    echo "$region"
}

if [ "$1" != "OnHotfixInstalled" ]; then
    PRODUCT_TYPE=$(get_environment_key "ProductType")
    log "ProductType:      $PRODUCT_TYPE"

    PRODUCT_REGION_RAW=$(get_product_region_raw)
    log "ProductRegionRaw:    $PRODUCT_REGION_RAW"

    MATCHED_ARCHIVE="${PRODUCT_REGION_RAW}".tar.gz
    log "Matched archive:  $MATCHED_ARCHIVE"

    LAST_PATCH_DEPLOYMENT_TIMESTAMP=$(get_last_patch_deployment)
    log "Last patch deployment timestamp:  $LAST_PATCH_DEPLOYMENT_TIMESTAMP"

    patch_loc
    patch_doc

    log "Disable agents..."
    disable_kav_agents

    log "Stopping kav_apps..."
    kill_kav_apps

    patch_binaries
    patch_kav_app
    patch_kav_agent

    log "Enable agents..."
    enable_kav_agents

    patch_updater
    patch_uninstaller

    update_config_xml
    stop_sysextctrld

    patch_configpdk
else
    LAST_PATCH_DEPLOYMENT_TIMESTAMP=$(get_last_patch_deployment)

    log "Last path deployment timestamp: $LAST_PATCH_DEPLOYMENT_TIMESTAMP"
fi

delete_old_stuff

log "Finished."
